module ActiveBackground
  
  # some methods to convert the keys from symbols to strings
  # and back again....
  # FIXME: Need to change the way configurations are processed (not when setting but when done)
  class Configuration
    include Singleton
    
    attr_accessor :environment, :initialization, :exclude, :process_order
    attr_reader :runned
    
    def init
      load root + "/lib/vendor/logger/reset_logger.rb"
      
      Dir.foreach(root+"/package/") do |package|
        file = root + '/package/' + package + '/' + package + '.rb'
        if File.exists?(file)
          load file
        end unless ['.', '..', '.svn'].include?(file)
      end
      
      @environment=:production
      @initialization=[:job, :worker, :handler]
      @process_order=[:rails, :database]
      @exclude=[:server_config, :temp_proxy]
      @runned=false
      @logger=LoggerCache.new
      @configs=Hash.new
      @server=Config::Server.new
      
      @logger.add_plain("\n\n############################################################################################\n")
      @logger.add_plain("##### Application has been started on #{Time.now} #####\n")
      @logger.add_plain("############################################################################################\n\n")
      
      ActiveBackground::Config.constants.each do |config|
        config_name=config.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
        unless @configs[config_name.to_sym]
          @configs[config_name.to_sym] = ActiveBackground::Config.const_get(config).new
          @logger.debug("Configuration: Adding configuration for #{config_name.to_s}")
        end
      end
      Thread.abort_on_exception=true
      $ACTIVE_BACKGROUND_DEBUG = true
    end
    
    def process
      @logger.logger(@logger_imp)
      @logger.process
    
      @logger.debug("Configuration: Starting processing configuration")
      @initialization.each do |config|
        @configs[config].process
      end
      
      @process_order.each do |config|
        @configs[config].process
      end
      
      @exclude_from_processing = @initialization | @exclude
      
      @configs.each do |name, config|
        unless @exclude_from_processing.include?(name) or !config.methods.include?('process')
          if config.class == ActiveBackground::Config::TempProxy and config.name
            begin
              @configs[name].config(ActiveBackground::Config.const_get(name).new)
              config.process
              @logger.debug("Configuration: Adding configuration for #{config.class.to_s}")
            rescue
              @logger.warn("Configuration: No configuration for #{config.name.to_s}") unless config.config
            end
          end
        end
      end
    end
        
    def server(server)
      if block_given?
        yield @server[server]
      else
        @server[server]
      end
    end
    alias :master :server
    
    # logging
    
    def logging(logdev, shift_age = 0, shift_size = 1048576)
      if block_given?
        @logger_imp = ActiveBackgroundLogger.new(logdev, shift_age, shift_size)
        yield @logger_imp
      else
        @logger_imp
      end
    end
    
    def logger
      @logger
    end
    
    def linux?
      if !defined(__human68k__) && !defined(_WIN32) && !defined(__MACOS__) && !defined(__EMX__) && !defined(__VMS)
        true
      else
        false
      end
    end
    
    def windows?
      if !defined(__human68k__) && !defined(_WIN32) && !defined(__MACOS__) && !defined(__EMX__) && !defined(__VMS)
        false
      else
        true
      end
    end
    
    def root
      File.expand_path(File.dirname(__FILE__) + "/../../")
    end
    
    def environment
      @environment.to_s
    end

    def installed_as_rails_plugin?
      File.exist?(File.dirname(__FILE__) + "/../../../../config/boot.rb")
    end
    
    private
      def method_missing(method, *args, &block)
        # TODO: if a config doesn't exist make a proxy and store the value's temporary
        if @configs[method]
          if block_given?
            yield @configs[method]
          else
            return @configs[method]
          end
        else
          @config[method] = Config::TempProxy.new
          @config[method].name=method
        end
      end

#    def setup(service = "Base")
#      server = Server.new
#      config = Configuration.instance
#      options = server.defaults
#      config_file = options[:config_file]
#      options[:service] = service
#      options.delete(:config_file)
#      
#      if config.installed_as_rails_plugin?
#        config.initialize_rails(:boot)
#        root = RAILS_ROOT
#      else
#        root = File.dirname(__FILE__) + "/../.."
#      end
#      
#      begin
#        if !File.exist?(root + "/config/#{config_file}")
#          File.open(root + "/config/#{config_file}", "w+") {|f| YAML.dump(config.dump(options), f)}
#        else
#          if !YAML.load_file(root + "/config/#{config_file}")
#            File.open(root + "/config/#{config_file}", "w+") {|f| YAML.dump(config.dump(options), f)}
#          else
#            tmp_options = options.merge(config.load(root + "/config/#{config_file}"))
#            File.open(root + "/config/#{config_file}", "w+") {|f| YAML.dump(config.dump(tmp_options), f)}
#          end
#        end
#      rescue Exception => e
#        puts 'setup failed ' + e.to_s
#      end
#    end
  end
  
  module Config
    class TempProxy
      def initialize
        @calls=Array.new
        @name=nil
      end
      
      def config(config=nil)
        if config
          @config=config
        else
          @config
        end
      end
      
      def name(name=nil)
        if name
          @name=name
        else
          @name
        end
      end
      
      def process
        @calls.each do |call|
          @logger.send(call[0], call[1], call[2])
        end
        @calls=Array.new
      end
      
      private
        def method_missing(method, *args, &block)
          unless @config
            @calls << [method, args, block]
          else
            process if @calls.size > 0
            @config.send(method, args)
          end
        end
    end
    
    class Server
      def initialize
        @servers=Hash.new
        @servers[:master]=Config::ServerConfig.new
        @servers[:master].name=:master
      end
      
      def[](key=nil)
        if @servers[key]
          @servers[key]
        elsif key
          @servers[key]=ServerConfig.new
        else
          @servers
        end
      end
      
      def process
      end
    end
    
    class ServerConfig
      attr_accessor :host, :port, :server_name, :background, :slave, :fallback, :sleep, :name
      
      def initialize
        @host='localhost'
        @port=rand(10000)
        @name=nil
        @server_name='ActiveBackground'
        @background=false
        @slave=false
        @fallback=false
        @sleep=0.01
      end
      
      def process
        raise 'You need to give each server a name' unless @name
      end
    end
    
    class Rails
      attr_accessor :enable, :root_dir, :config
      
      def initialize
        @enable = false
        @root_dir = File.dirname(__FILE__) + "/../../../../../"
        @config = File.dirname(__FILE__) + "/../../../../../config/boot.rb"
      end
      
      def status
        @enable
      end
      
      def process
        if @enable
          require @config
          config = open("#{File.dirname(__FILE__)}/../../../../../config/database.yml") { |f| YAML.load(f) }
          ActiveBackground::Configuration.instance.database.connect_hash(config[ActiveBackground::Configuration.instance.environment])
        end
      end
    end
    
    class AccessControl
      attr_accessor :allow, :disallow
      
      def initialize
        @allow = 'localhost'
        @disallow = '*'
      end
    end
    
    class Handler
      def initialize
        @handlers = [:cache, :job]
        @handler_objects = Hash.new
        @dirs = [ActiveBackground::Configuration.instance.root+"/lib/active_background/handlers/"]
        @files = Array.new
      end
      
      def add(handler)
        @handlers << handler
      end
      
      def dir(dir)
        @dirs << dir
      end
      
      def file(file)
        @files << file
      end
      
      def get_all
        handlers=Hash.new
        @handler_objects.collect{|key, handler| handlers[key]=handler if @handlers.include?(key)}
        return handlers
      end
      
      def process
        # load the handlers
        @dirs.each do |dir|
          Dir.foreach(dir) do |file|
            unless ['.', '..', '.svn'].include?(file)
              load dir + file
              handler = file.split('.')[0]
              handler_object = ActiveBackground::Handler.const_get(handler.to_s.split('_').inject('') {|klass_name, slice| klass_name  << slice.capitalize})
              @handler_objects[handler.to_sym]=handler_object.new
            end
          end
        end
        @files.each do |file|
          dir = File.dirname(file) + '/'
          file = file.split(dir)[1]
          unless ['.', '..', '.svn'].include?(file)
            load dir + file
            handler = file.split('.')[0]
            handler_object = ActiveBackground::Handler.const_get(handler.to_s.split('_').inject('') {|klass_name, slice| klass_name  << slice.capitalize})
            @handler_objects[handler.to_sym]=handler_object.new
          end
        end
      end
    end
      
    class Database
      attr_accessor :lib_type, :maximum_connections, :adapter, :host, :username, :password, :database
      
      def initialize
        @lib_type = nil
        @maximum_connections = 50
        @adapter=nil
        @host=nil
        @username=nil
        @password=nil
        @database=nil
        @file = Array.new
      end
          
      def connect_hash(connect=nil)
        unless connect
          config = {
                  'adapter' => @adapter,
                  'host' => @host,
                  'username' => @username,
                  'database' => @database
                  }
          config.merge!({'password' => @password}) if @password
          return config
        else
          @adapter = connect['adapter'] if connect['adapter']
          @host = connect['host'] if connect['host']
          @username = connect['username'] if connect['username']
          @database = connect['database'] if connect['database']
          @password = connect['password'] if connect['password']
        end
      end
      alias :connect_hash= :connect_hash
      
      def process
        config = ActiveBackground::Configuration.instance
        unless config.rails.enable
          Dir.foreach(File.expand_path(File.dirname(__FILE__) + "/../../workspace/database/active_record/")) do |file|
            unless ['.', '..', '.svn'].include?(file)
              require File.expand_path(File.dirname(__FILE__) + "/../../workspace/database/active_record/" + file) unless @file.include?(file)
              @file << file
            end
          end
        end
      end
    end
    
    class Job
      def initialize
        @options = { :terminate => true,
                     :archive => false,
                     :postpone_termination => false,
                     :before_job => false,
                     :after_job => false,
                     :cleanup => false,
                     :archive => true,
                     :ttl => nil }
        @directories = Array.new
        
        dir(ActiveBackground::Configuration.instance.root+'/workspace/jobs/')
      end
      
      def dir(dir)
        @directories << dir unless @directories.include?(dir)
      end
      alias :dir= :dir
      alias :directory :dir
      alias :directory= :dir
      alias :register :dir
      alias :register_dir :dir
      
      def dirs
        @directories
      end
      alias :directories :dirs
      alias :registered_dirs :dirs
      alias :registered_directories :dirs
      
      def process
      end
      
      def method_missing(method, *args)
        @options[method] = args
      end
    end
    
    class Worker < Job
      def process
        config = ActiveBackground::Configuration.instance
        config.job.dir=config.root + "/workspace/workers/"
      end
    end
  end
end